package com.liuc.practice.ipersistent.config;

import com.liuc.practice.ipersistent.io.Resources;
import com.liuc.practice.ipersistent.pojo.SqlMappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 由于 sqlMappedStatement 对应 sqlMapConfig.xml 中的 mapper 部分
 * 和数据库配置不同，所以解析 <mapper></mapper> 部分
 * 并封装 SqlMappedStatement 对象
 * 根据单一职责原则应该独立为一个类来对该部分数据做处理
 *
 * @Author Liu Chang
 * @Date 2020/12/13 6:58 上午
 */
public class SqlMappedStatementMapBuilder {

    /**
     * 目的：封装 Map<String, SqlMappedStatement> 对象
     * 1. 解析出每个 mapper 中的 resourcePath: xxxMapper.xml
     * 2. 解析每个 xxxMapper.xml 中的标签，并封装为 SqlMappedStatement 对象
     * 
     * @param elements sqlMapConfig.xml 中所有的 <mapper></mapper>
     * @return
     */
    public static Map<String, SqlMappedStatement> build(List<Element> elements) throws DocumentException {
        Map<String, SqlMappedStatement> map = new HashMap<>();

        for (Element element : elements) {
            // 获取到每一个 mapper 中的 resource
            String resourcePath = element.attributeValue("resource");
            InputStream mapperInputStream = Resources.getResourcesAsInputStream(resourcePath);
            map = parse(mapperInputStream);
        }

        return map;
    }

    /**
     * 目的：解析 xxxMapper.xml 文件
     * 每个 xxxMapper.xml 中都会有多个
     * <select></select>
     * <insert></insert>
     * <update></update>
     * <delete></delete>
     * 
     * @param mapperInputStream：xxxMapper.xml 文件输入流
     * @return
     * @throws DocumentException
     */
    private static Map<String, SqlMappedStatement> parse(InputStream mapperInputStream) throws DocumentException {
        Map<String, SqlMappedStatement> map = new HashMap<>();

        Document document = new SAXReader().read(mapperInputStream);

        // 获取到的是 <mapper nameSpace = "user"></mapper>
        Element rootElement = document.getRootElement();
        String nameSpace = rootElement.attributeValue("nameSpace");

        List<Element> selectMethods = rootElement.selectNodes("//select");
        Map<String, SqlMappedStatement> select = convert2Map(nameSpace, selectMethods, "select");
        map.putAll(select);

        List<Element> insertMethods = rootElement.selectNodes("//insert");
        Map<String, SqlMappedStatement> insert = convert2Map(nameSpace, insertMethods, "insert");
        map.putAll(insert);

        List<Element> updateMethods = rootElement.selectNodes("//update");
        Map<String, SqlMappedStatement> update = convert2Map(nameSpace, updateMethods, "update");
        map.putAll(update);

        List<Element> deleteMethods = rootElement.selectNodes("//delete");
        Map<String, SqlMappedStatement> delete = convert2Map(nameSpace, deleteMethods, "delete");
        map.putAll(delete);

        return map;
    }

    private static Map<String, SqlMappedStatement> convert2Map(String nameSpace, List<Element> list, String executeType) {
        Map<String, SqlMappedStatement> map = new HashMap<>();

        for (Element element : list) {
            SqlMappedStatement statement = new SqlMappedStatement();
            statement.setId(element.attributeValue("id"));
            statement.setResultType(element.attributeValue("resultType"));
            statement.setParameterType(element.attributeValue("parameterType"));
            statement.setSql(element.getTextTrim());
            statement.setExecuteType(executeType);

            String statementId = nameSpace + "." + element.attributeValue("id");
            map.put(statementId, statement);
        }
        return map;
    }
}
